﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using PocketMahjong.Core;
using System.Drawing.Imaging;
using System.Diagnostics;
using System.Reflection;

namespace PocketMahjong
{
	public partial class BoardView : Control
	{
		private struct PositionInfo
		{
			private Position _position;
			public Position Position
			{
				get { return _position; }
				set { _position = value; }
			}

			private Rectangle _bounds;
			public Rectangle Bounds
			{
				get { return _bounds; }
				set { _bounds = value; }
			}

			public PositionInfo(Position pos, Rectangle rect)
			{
				_position = pos;
				_bounds = rect;
			}
		}

		private const int CellWidth = 32;
		private const int CellHeight = 45;
		private const int TileWidth = 36;
		private const int TileHeight = 49;
		private const int StatusBarHeight = 40;

		private readonly Brush LightGreenBrush = new SolidBrush(Color.LightGreen);
		private readonly Brush BlackBrush = new SolidBrush(Color.Black);

		Stopwatch _timer = new Stopwatch();
		private Bitmap _boardImage;
		private Graphics _gr;
		private Dictionary<Tile, Bitmap> _tiles;
		private Dictionary<Tile, Bitmap> _tilesSelected;
		private Bitmap _tile1_top, _tile1_bottom;
		private Bitmap _tile2_top, _tile2_bottom;
		private ImageAttributes _tileAttr;
		private List<PositionInfo> _bounds = new List<PositionInfo>();

		private Board _board;
		public Board Board
		{
			get { return _board; }
			set
			{
				_board = value;
				Draw();
				Invalidate();
			}
		}

		private BoardController _controller;
		internal BoardController Controller
		{
			get { return _controller; }
		}

		public BoardView()
		{
			InitializeComponent();
			LoadImages();
			_controller = new BoardController(this);
		}

		protected override void OnResize(EventArgs e)
		{
			_boardImage = null;
			Draw();
			base.OnResize(e);
		}

		private void LoadImages()
		{
			_tiles = new Dictionary<Tile, Bitmap>();
			_tilesSelected = new Dictionary<Tile, Bitmap>();
			_tile1_top = Properties.Resources.Tile1_1;
			_tile1_bottom = Properties.Resources.Tile1_3;
			_tile2_top = Properties.Resources.Tile2_1;
			_tile2_bottom = Properties.Resources.Tile2_3;
			foreach (Tile t in TileSet.GetAllTiles())
			{
				AddTile(t, _tiles, Properties.Resources.Tile1_2);
				AddTile(t, _tilesSelected, Properties.Resources.Tile2_2);
			}

			_tileAttr = new ImageAttributes();
			var tc = _tile1_top.GetPixel(0, 0);
			_tileAttr.SetColorKey(tc, tc);
		}

		private void AddTile(Tile t, Dictionary<Tile, Bitmap> collection, Bitmap tile)
		{
			if (!collection.ContainsKey(t))
			{
				Assembly asm = Assembly.GetExecutingAssembly();
				var ns = "PocketMahjong.Resources.";
				var face = new Bitmap(asm.GetManifestResourceStream(ns + string.Format("{0}{1}.png", t.Kind, t.Number)));
				using (Graphics gr = Graphics.FromImage(tile))
					gr.DrawImage(face, (tile.Width - face.Width) / 2 + 2, (tile.Height - face.Height) / 2);
				collection.Add(t, tile);
			}
		}

		public IEnumerable<Cell> DrawList
		{
			get
			{
				for (int lay = 0; lay < _board.LayerCount; lay++)
				{
					for (int col = _board.ColumnCount; col >= 0; col--)
					{
						for (int row = 0; row < _board.RowCount; row++)
						{
							var pos = new Position(lay, row, col);
							var tile = _board[lay, row, col];
							if (tile != null)
								yield return new Cell(pos, tile);
						}
					}
				}

			}
		}

		private void Draw()
		{
			if (ClientRectangle.Width <= 0 || ClientRectangle.Height <= 0)
				return;
			if (_boardImage == null)
			{
				_boardImage = new Bitmap(ClientRectangle.Width, ClientRectangle.Height);
				_gr = Graphics.FromImage(_boardImage);
			}

			_bounds.Clear();
			_gr.Clear(Color.DarkGreen);
			if (_board == null)
				return;

			DrawTiles();
			DrawStatus();
		}

		private void DrawStatus()
		{
			var str = string.Format("Tiles: {0}   Pairs: {1}", _board.TilesCount, _board.PayersCount);
			Rectangle rect = new Rectangle(0, Height - StatusBarHeight, Width, StatusBarHeight);
			_gr.FillRectangle(LightGreenBrush, rect);
			_gr.DrawString(str, Font, BlackBrush, 0, rect.Y);
		}

		private void DrawTiles()
		{
			int sx = (int)((_boardImage.Width - Math.Ceiling(_board.ColumnCount / 2.0) * CellWidth) / 2);
			int sy = (int)((_boardImage.Height - StatusBarHeight - Math.Ceiling(_board.RowCount / 2.0) * CellHeight) / 2);
			foreach (Cell cell in DrawList)
			{
				var pos = cell.Position;
				Image face, top, bottom;
				if (_board.Selection.Contains(pos))
				{
					face = _tilesSelected[cell.Tile];
					top = _tile2_top;
					bottom = _tile2_bottom;
				}
				else
				{
					face = _tiles[cell.Tile];
					top = _tile1_top;
					bottom = _tile1_bottom;
				}
				int x = sx + pos.Column * CellWidth / 2 + pos.Layer * 4;
				int y = sy + pos.Row * CellHeight / 2 - pos.Layer * 4;

				DrawImageTransparent(top, x, y);
				_gr.DrawImage(face, x, y + top.Height);
				DrawImageTransparent(bottom, x, y + top.Height + face.Height);

				_bounds.Add(new PositionInfo(cell.Position, new Rectangle(x, y, TileWidth, TileHeight)));
			}
		}

		private void DrawImageTransparent(Image image, int x, int y)
		{
			var rect = new Rectangle(x, y, image.Width, image.Height);
			_gr.DrawImage(image, rect, 0, 0, rect.Width, rect.Height, GraphicsUnit.Pixel, _tileAttr);
		}

		protected override void OnMouseDown(MouseEventArgs e)
		{
			var pt = new Point(e.X, e.Y);
			for (int i = _bounds.Count - 1; i >= 0; i--)
			{
				if (_bounds[i].Bounds.Contains(pt))
				{
					_controller.ClickTile(_bounds[i].Position);
					break;
				}
			}
		}

		protected override void OnPaintBackground(PaintEventArgs e) { }

		protected override void OnPaint(PaintEventArgs e)
		{
			#if DEBUG
			_timer.Reset();
			_timer.Start();
			#endif

			Draw();
			if (_boardImage != null)
				e.Graphics.DrawImage(_boardImage, 0, 0);

			#if DEBUG
			_timer.Stop();
			var res = _timer.ElapsedMilliseconds;
			e.Graphics.DrawString(res.ToString(), Font, BlackBrush, 0, 0);
			#endif
		}

		internal void UpdateView()
		{
			Invalidate();
		}
	}
}
